package com.xbp.examsystem.service.impl;



import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xbp.examsystem.entity.ExamRecord;
import com.xbp.examsystem.entity.Paper;
import com.xbp.examsystem.entity.PaperQuestion;
import com.xbp.examsystem.entity.Question;
import com.xbp.examsystem.exception.ExamException;
import com.xbp.examsystem.mapper.ExamRecordMapper;
import com.xbp.examsystem.mapper.PaperMapper;
import com.xbp.examsystem.mapper.PaperQuestionMapper;
import com.xbp.examsystem.mapper.QuestionMapper;
import com.xbp.examsystem.service.ExamRecordService;
import com.xbp.examsystem.service.PaperQuestionService;
import com.xbp.examsystem.service.PaperService;
import com.xbp.examsystem.service.QuestionService;
import com.xbp.examsystem.vo.AiPaperVo;
import com.xbp.examsystem.vo.PaperVo;
import com.xbp.examsystem.vo.RuleVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 试卷服务实现类
 */
@Slf4j
@Service
@Transactional
public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements PaperService {


    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private PaperQuestionService paperQuestionService;
    @Autowired
    private ExamRecordMapper examRecordMapper;


    @Override
    public Paper getPaperDetailsById(Integer id) {
        //添加这个方法是因为之前那个方法没法获取试卷中题目的分数，之前那个方法是getquestiondetailsbyid
        List<Question> questionList = questionMapper.getQuestionListByPaperId(id);
        Paper paper = baseMapper.selectById(id);
        //根据类型排序
        questionList.sort(Comparator.comparing(Question::getType));
        paper.setQuestions(questionList);

        return paper;
    }

    @Override
    public Paper createPaper(PaperVo paperVo) {
        boolean b = baseMapper.exists(new LambdaQueryWrapper<Paper>().eq(Paper::getName, paperVo.getName()));
        if (b) {
            throw new ExamException( 345, "试卷已存在");
        }
        Paper paper = new Paper();
        BeanUtils.copyProperties(paperVo, paper);
        paper.setStatus("DRAFT");
        Map<Long, BigDecimal> questions = paperVo.getQuestions();
        //设置题目数量
        paper.setQuestionCount(questions.size());
        //设置总分
        BigDecimal bigDecimal = questions.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        paper.setTotalScore(bigDecimal);
        baseMapper.insert(paper);
        /*List<PaperQuestion> collect = questions.entrySet().stream().map(entry -> {
            PaperQuestion paperQuestion = new PaperQuestion();
            paperQuestion.setPaperId(paper.getId());
            paperQuestion.setQuestionId(entry.getKey());
            paperQuestion.setScore(entry.getValue());
            return paperQuestion;
        }).collect(Collectors.toList());*/

        List<PaperQuestion> paperQuestionList = questions.entrySet().stream().map(entry -> new PaperQuestion(paper.getId(), entry.getKey(), entry.getValue())).collect(Collectors.toList());

        /*ArrayList<PaperQuestion> paperQuestions = new ArrayList<>();
        questions.forEach((key, value) -> {
            PaperQuestion paperQuestion = new PaperQuestion();
            paperQuestion.setPaperId(paper.getId());
            paperQuestion.setQuestionId(key);
            paperQuestion.setScore(value);
            paperQuestions.add(paperQuestion);
        });*/
        paperQuestionService.saveBatch(paperQuestionList);
        return paper;

    }

    @Override
    public Paper updatePaper(Integer id, PaperVo paperVo) {
        Paper paper = baseMapper.selectById(id);
        if(paper.getStatus().equals("PUBLISHED")){
            throw new ExamException( 345, "已发布试卷不允许修改");
        }
        LambdaQueryWrapper<Paper> ne = new LambdaQueryWrapper<Paper>().eq(Paper::getName, paperVo.getName()).ne(Paper::getId, id);
        if (baseMapper.exists(ne)) {
            throw new ExamException( 345, "试卷已存在");
        }
        BeanUtils.copyProperties(paperVo, paper);
        paper.setUpdateTime(new Date());
        Map<Long, BigDecimal> questions = paperVo.getQuestions();
        paper.setQuestionCount(questions.size());
        BigDecimal bigDecimal = questions.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        paper.setTotalScore(bigDecimal);
        baseMapper.updateById(paper);
        paperQuestionService.remove(new LambdaQueryWrapper<PaperQuestion>().eq(PaperQuestion::getPaperId, id));
        List<PaperQuestion> paperQuestionList = questions.entrySet().stream().map(entry -> new PaperQuestion(paper.getId(), entry.getKey(), entry.getValue())).collect(Collectors.toList());
        paperQuestionService.saveBatch(paperQuestionList);
        return paper;
    }

    @Override
    public void updatePaperStatus(Integer id, String status) {
        Long l = examRecordMapper.selectCount(new LambdaQueryWrapper<ExamRecord>().eq(ExamRecord::getExamId, id).eq(ExamRecord::getStatus, "进行中"));
        if(l>0){
            throw new ExamException( 345, "有进行中的考试，请先结束考试");
        }
        baseMapper.update(null, new LambdaUpdateWrapper<Paper>().eq(Paper::getId, id).set(Paper::getStatus, status));

    }

    @Override
    public void deletePaperById(Integer id) {
        Long l = examRecordMapper.selectCount(new LambdaQueryWrapper<ExamRecord>().eq(ExamRecord::getId, id).eq(ExamRecord::getStatus, "进行中"));
        if(l>0){
            throw new ExamException( 345, "有进行中的考试，请先结束考试");
        }
        baseMapper.deleteById(id);
        paperQuestionService.remove(new LambdaQueryWrapper<PaperQuestion>().eq(PaperQuestion::getPaperId, id));

    }

    @Override
    public Paper customAiCreatePaper(AiPaperVo aiPaperVo) {
        //1. 试卷的基本属性赋值并保存 （名字 描述 时间 状态）
        Paper paper = new Paper();
        BeanUtils.copyProperties(aiPaperVo,paper);
        paper.setStatus("DRAFT");
        save(paper);

        //2. 组卷规则下的试题选择和中间表的保存
        int questionCount = 0;
        BigDecimal totalScore = BigDecimal.ZERO;
        for (RuleVo rule : aiPaperVo.getRules()) {
            //步骤1：校验规则下的题目数量 = 0 跳过
            if (rule.getCount() == 0){
                log.warn("在：{}类型下，不需要出题！",rule.getType().name());
                continue;
            }
            //步骤2：查询当前规则下的所有题目集合 【type categoryIds】
            LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Question::getType, rule.getType().name());
            queryWrapper.in(!ObjectUtils.isEmpty(rule.getCategoryIds()),Question::getCategoryId, rule.getCategoryIds());
            List<Question> allQuestionList = questionMapper.selectList(queryWrapper);

            //步骤3：校验查询的题目集合，集合为空！跳过本次！
            if (ObjectUtils.isEmpty(allQuestionList)){
                log.warn("在：{}类型下，我们指定的分类：{},没有查询到题目信息！",rule.getType().name(),rule.getCategoryIds());
                continue;
            }

            //步骤4：判断下是否有规则下count数量！ 没有要全部了
            int realNumbers = Math.min(rule.getCount(), allQuestionList.size());

            //步骤5：本次规则下添加的数量和分数累加
            questionCount += realNumbers;
            totalScore =  totalScore.add(BigDecimal.valueOf((long) realNumbers * rule.getScore()));

            //步骤6：先打乱数据，再截取需要题目数量
            Collections.shuffle(allQuestionList);
            List<Question> realQuestionList = allQuestionList.subList(0, realNumbers);

            //步骤7：转成中间表并进行保存
            List<PaperQuestion> paperQuestionList = realQuestionList.stream().map(question ->
                    new PaperQuestion(paper.getId(), question.getId(), BigDecimal.valueOf(rule.getScore()))
            ).collect(Collectors.toList());
            paperQuestionService.saveBatch(paperQuestionList);
        }
        //3. 修改试卷信息（总题数，总分数）
        paper.setQuestionCount(questionCount);
        paper.setTotalScore(totalScore);
        updateById(paper);
        //4. 返回试卷对象
        return paper;
    }
}